package cn.fx.framework.redis;

import cn.fx.framework.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 自定义RedisTemplate,对Spring的redisTemplate进行封装：
 * 默认cache使用set()、get()、delete(),hashPut(),hashGet(),hashDelete()
 * 不同前缀缓存putCacheObject(),getCacheObject(),evictCache(),evictCaches(),clearCache().
 * @author ljh
 * @date 2018-09-02 21:42
 */
public class MyRedisTemplate {

    private static final Logger log = LoggerFactory.getLogger(MyRedisTemplate.class);
    private String keyPrefix;
    private RedisTemplate<String,Object> redisTemplate;
    private MyRedisCacheManager cacheManager;

    public MyRedisTemplate(String keyPrefix,RedisTemplate<String,Object> redisTemplate,MyRedisCacheManager cacheManager){
        this.keyPrefix = keyPrefix;
        if(!this.keyPrefix.endsWith(":")){
            this.keyPrefix += ":";
        }
        this.redisTemplate = redisTemplate;
        this.cacheManager = cacheManager;
    }

    public String getKeyPrefix(){
        return this.keyPrefix;
    }

    /**
     * 默认cache增加
     * @param key
     * @param value
     */
    public void set(String key, Object value){
        redisTemplate.opsForValue().set(withKey(key),value);
    }

    /**
     * 默认cache增加
     * @param key
     * @param value
     */
    public void set(String key, Object value, long times, TimeUnit timeUnit){
        redisTemplate.opsForValue().set(withKey(key),value,times,timeUnit);
    }

    /**
     * 默认cache获取
     * @param key
     */
    public Object get(String key){
        String id = withKey(key);
        return redisTemplate.opsForValue().get(id);
    }

    public Object getAndSet(String key, Object value){
        return redisTemplate.opsForValue().getAndSet(withKey(key),value);
    }

    /**
     * 删除默认缓存中指定的元素
     * @param key
     * @return
     */
    public boolean delete(String key){
        return redisTemplate.delete(withKey(key));
    }

    /**
     * 自增，按1增长
     * @param key
     * @return
     */
    public long incr(String key){
        return incrBy(withKey(key),1);
    }

    /**
     * 按increment增长
     * @param key
     * @param increment
     * @return
     */
    public long incrBy(String key,long increment){
        Long l =  redisTemplate.opsForValue().increment(withKey(key), increment);
        if(l == null){
            return 1L;
        }
        return l;
    }

    /**
     * 将对象放到缓存管理器中
     * @param cacheName 缓存管理器中的缓存名称
     * @param key 缓存项key
     * @param object 缓存的对象
     */
    public void putCacheObject(String cacheName, String key, Object object){
        cacheManager.getCache(cacheName).put(key,object);
    }
    /**
     * 将缓存管理器中缓存的对象失效
     * @param cacheName 缓存管理器中的缓存名称
     * @param key 缓存项key
     */
    public void evictCache(String cacheName,String key){
        cacheManager.getCache(cacheName).evict(key);
    }

    /**
     * 批量清除缓存管理器中缓存
     * @param cacheName
     * @param keys
     */
    public void evictCaches(String cacheName,Set<String> keys){
        Cache cache = cacheManager.getCache(cacheName);
        if(cache != null && keys.size() > 0){
            for(String key : keys){
                cache.evict(key);
            }
        }
    }
    /**
     * 从缓存管理器中获取缓存的对象
     * @param cacheName 缓存管理器中的缓存名称
     * @param key 缓存项key
     */
    public Object getCacheObject(String cacheName,String key){
        Cache.ValueWrapper vw = cacheManager.getCache(cacheName).get(key);
        if(vw == null){
            return null;
        }
        return vw.get();
    }

    //将指定名称缓存对象中的缓存全部清除
    public void clearCache(String cacheName){
        cacheManager.getCache(cacheName).clear();
    }

    /**
     * 将缓存的key之前增加项目缓存整体前辍
     * @param key
     * @return
     */
    private String withKey(String key){
        return this.keyPrefix + key;
    }

    /**
     * 向redis指定队列中发送一条消息
     * @param channel
     * @param message
     */
    public void convertAndSend(String channel, Object message){
        redisTemplate.convertAndSend(channel,message);
    }

    /**
     * 加锁
     * @param key
     * @param value 过期时间戳
     * @return
     */
    public boolean lock(String key,String value){
        if(redisTemplate.opsForValue().setIfAbsent(key,value)){//对应setnx命令
            //可以成功设置,也就是key不存在
            return true;
        }

        //判断锁超时 - 防止原来的操作异常，没有运行解锁操作  防止死锁
        String currentValue = (String)redisTemplate.opsForValue().get(key);
        //如果锁过期
        if(!StringUtils.isNullOrEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){//currentValue不为空且小于当前时间
            //获取上一个锁的时间value
            String oldValue =(String)redisTemplate.opsForValue().getAndSet(key,value);//对应getset，如果key存在

            //假设两个线程同时进来这里，因为key被占用了，而且锁过期了。获取的值currentValue=A(get取的旧的值肯定是一样的),两个线程的value都是B,key都是K.锁时间已经过期了。
            //而这里面的getAndSet一次只会一个执行，也就是一个执行之后，上一个的value已经变成了B。只有一个线程获取的上一个值会是A，另一个线程拿到的值是B。
            if(!StringUtils.isNullOrEmpty(oldValue) && oldValue.equals(currentValue) ){
                //oldValue不为空且oldValue等于currentValue，也就是校验是不是上个对应的商品时间戳，也是防止并发
                return true;
            }
        }
        return false;
    }


    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key,String value){
        try {
            String currentValue = (String)redisTemplate.opsForValue().get(key);
            if(!StringUtils.isNullOrEmpty(currentValue) && currentValue.equals(value) ){
                redisTemplate.opsForValue().getOperations().delete(key);//删除key
            }
        } catch (Exception e) {
            log.error("[Redis分布式锁] 解锁出现异常了，{}",e);
            e.printStackTrace();
        }
    }

    /**
     * 默认缓存增加Hash数据
     * @param key
     * @param hashKey
     * @param value
     */
    public void hashPut(String key, String hashKey, Object value){
        redisTemplate.opsForHash().put(withKey(key),hashKey,value);
    }

    /**
     * 默认缓存获取Hash数据
     * @param key
     * @param hashKey
     * @return
     */
    public Object hashGet(String key, String hashKey){
        return redisTemplate.opsForHash().get(withKey(key),hashKey);
    }

    /**
     * 默认缓存删除Hash数据
     * @param key
     * @param hashKey
     */
    public void hashDelete(String key, String hashKey){
        redisTemplate.opsForHash().delete(withKey(key), hashKey);
    }

    /**
     * 默认缓存删除Hash数据
     * @param key
     */
    public void hashDelete(String key){
        String _key = withKey(key);
        boolean b = redisTemplate.delete(_key);
        System.out.println(b);
    }

    /**
     * 设置缓存的过期时间
     * @param key
     * @param timeout
     * @param timeUnit
     */
    public void expire(String key, long timeout, TimeUnit timeUnit){
        redisTemplate.expire(withKey(key),timeout,timeUnit);
    }
}
